﻿// -------------------------------------------------------------------------
//    @FileName         :    NFMoneyOp.h
//    @Author           :    gaoyi
//    @Date             :    2025/1/2
//    @Email            :    445267987@qq.com
//    @Module           :    NFMoneyOp
//
// -------------------------------------------------------------------------

// --- 文件总结 ---------------------------------------------------------------
// `NFMoneyOp` 是金钱操作类：
// - 继承自 NFBagBaseOp，提供提交语义和嵌套创建防护
// - 支持金钱的增加、减少、修改操作
// - 支持尽量操作和必须操作两种模式
// - 提供金钱上限检查、负数检查等安全机制
// - 集成日志记录功能
// - 区分虚拟货币和真实货币的提交逻辑
// ---------------------------------------------------------------------------

#pragma once

#include <NFLogicCommon/NFItemDefine.h>

#include "NFLogicCommon/BagPage/NFBagBaseOp.h"
#include "proto_log.nanopb.h"

class NFPlayer;
class NFBagPageOpMgr;

/**
 * @brief 金钱操作类
 * 提供金钱的增删改查和提交功能，支持多种金钱类型的操作
 */
class NFMoneyOp : public NFBagBaseOp<NFMoneyOp>
{
    friend NFBagPageOpMgr;
public:
    /**
     * @brief 构造函数
     * @param pPlayer 玩家指针
     * @param iOpReason 操作原因
     * @param bNeedCommit 是否需要提交（默认true）
     */
    NFMoneyOp(NFPlayer* pPlayer, int iOpReason, bool bNeedCommit = true);

    /**
     * @brief 析构函数
     */
    ~NFMoneyOp() override;
protected:
    /** @note 禁止复制：复制会导致莫名问题，比如操作一半了复制给另一个，commit时会覆盖 */
    NFMoneyOp(const NFMoneyOp&) = delete;
    NFMoneyOp& operator=(const NFMoneyOp&) = delete;

public:
    /**
     * @brief 设置为可以减少金钱到负数
     */
    void SetCanDecToLessZero();
    
    /**
     * @brief 不检测获取上限
     */
    void SetIgnoreLimitCheck() { m_bIgnoreLimit = true; }
    
    /**
     * @brief 设置日志参数
     * @param llLogParam 日志参数
     */
    void SetLogParam(int64_t const llLogParam) { m_llLogParam = llLogParam; }
    
    /**
     * @brief 设置子原因
     * @param iSubReason 子原因
     */
    void SetSubReason(int iSubReason) { m_iSubReason = iSubReason; }

public:
    /**
     * @brief 增加金钱
     * @param enMoneyType 金钱类型
     * @param llMoney 想要增加的金钱数（must >= 0）
     * @param pAddedNum 输出实际增加数量（=nullptr 全部增加，!=nullptr 尽可能增加）
     * @return 0成功，非0失败
     * @note 即使增加的数量和llMoney不一致，也算成功
     */
    int Inc(int enMoneyType, int64_t llMoney, int64_t* pAddedNum = nullptr);
    
    /**
     * @brief 尽量增加金钱（输出详细信息版本）
     * @param enMoneyType 金钱类型
     * @param llMoney 想要增加的金钱数（must >= 0）
     * @param llRealIncMoney 输出实际增加的金钱数（>=0）
     * @param llNewMoney 输出增加后的金钱数
     * @return 0成功，非0失败
     */
    int Inc(int enMoneyType, int64_t llMoney, int64_t& llRealIncMoney, int64_t& llNewMoney);

    /**
     * @brief 减少金钱
     * @param enMoneyType 金钱类型
     * @param llMoney 想要减少的金钱数（must >= 0）
     * @param piDelNum 输出实际减少数量（=NULL 全部减少，!=NULL 尽可能减少）
     * @return 0成功，非0失败
     */
    int Dec(int enMoneyType, int64_t llMoney, int64_t* piDelNum = NULL);
    
    /**
     * @brief 减少所有金钱
     * @param enMoneyType 金钱类型
     * @param piDelNum 输出实际减少数量
     * @return 0成功，非0失败
     */
    int DecAll(int enMoneyType, int64_t* piDelNum = NULL);
    
    /**
     * @brief 尽量减少金钱（输出详细信息版本）
     * @param enMoneyType 金钱类型
     * @param llMoney 想要减少的金钱数（must >= 0）
     * @param llRealDecMoney 输出实际减少的金钱数（>=0）
     * @param llNewMoney 输出减少后的金钱数
     * @return 0成功，非0失败
     */
    int Dec(int enMoneyType, int64_t llMoney, int64_t& llRealDecMoney, int64_t& llNewMoney);

    /**
     * @brief 获取被操作后的金钱数
     * @param enMoneyType 金钱类型
     * @param llMoney 输出金钱数
     * @return 0成功，非0失败
     */
    int GetMoney(int enMoneyType, int64_t& llMoney);

    /**
     * @brief 提交操作（未提交前所有操作都不生效）
     * @param bSyncToClient 是否同步到客户端
     * @param preCheckCommit 是否预检查提交
     * @return 0成功，非0失败
     */
    int Commit(bool bSyncToClient, bool preCheckCommit = false);
    
    /**
     * @brief 填充背包添加物品通知消息（金钱部分）
     * @param pNtf 输出通知消息
     * @return 0成功，非0失败
     */
    int FillPackageInfoForAdd(proto_ff::Proto_CSRoleItemNtf* pNtf);
    
    /**
     * @brief 填充背包删除物品通知消息（金钱部分）
     * @param pNtf 输出通知消息
     * @return 0成功，非0失败
     */
    int FillPackageInfoForDel(proto_ff::Proto_CSRoleItemNtf* pNtf);
public:
    /**
     * @brief 修改金钱（可增可减）
     * @param enMoneyType 金钱类型
     * @param llMoney 想要修改的金钱数（>0加钱，<0减钱，=0无操作）
     * @return 0成功，非0失败
     * @note 增加数量不一致算成功
     */
    int Modify(int enMoneyType, int64_t llMoney);
    
    /**
     * @brief 尽量修改金钱（输出详细信息版本）
     * @param enMoneyType 金钱类型
     * @param llMoney 想要修改的金钱数（>0加钱，<0减钱，=0无操作）
     * @param llRealChangeMoney 输出实际修改的金钱数
     * @param llNewMoney 输出修改后的金钱数
     * @return 0成功，非0失败
     */
    int Modify(int enMoneyType, int64_t llMoney, int64_t& llRealChangeMoney, int64_t& llNewMoney);

public:
    /**
     * @brief 检测金钱类型是否合法
     * @param enMoneyType 金钱类型
     * @return 0合法，非0不合法
     */
    int CheckMoneyType(int enMoneyType);

    /**
     * @brief 设置金钱数量
     * @param enMoneyType 金钱类型
     * @param llMoney 金钱数量
     * @return 0成功，非0失败
     */
    int SetMoney(int enMoneyType, int64_t llMoney);

    /**
     * @brief 将变更过的金钱同步到客户端
     * @return 0成功，非0失败
     */
    int SyncToClient();

    /**
     * @brief 货币扣除操作，至多能扣除多少的检查
     * @param enMoneyType 金钱类型
     * @param llNewMoney 新的金钱数
     * @return 0成功，非0失败
     * @note 各业务自己添加检查逻辑
     */
    int MoneyDecNoMoreThanCheck(int enMoneyType, int64_t llNewMoney);

public:
    /**
     * @brief 查找金钱变更记录
     * @param enMoneyType 金钱类型
     * @return 金钱变更记录指针，未找到返回nullptr
     */
    MoneyChange* FindMoney(int enMoneyType);
    
    /**
     * @brief 创建金钱变更记录
     * @param enMoneyType 金钱类型
     * @param iMoneyNum 金钱数量
     * @return 金钱变更记录指针
     */
    MoneyChange* CreateMoney(int enMoneyType, int64_t iMoneyNum);

    /**
     * @brief 获取玩家指针
     * @return 玩家指针
     */
    NFPlayer* GetPlayer() { return m_pPlayer; }
    
    /**
     * @brief 获取玩家指针（const版本）
     * @return 玩家指针
     */
    const NFPlayer* GetPlayer() const { return m_pPlayer; }

    /**
     * @brief 获取所有者ID
     * @return 所有者ID
     */
    uint64_t GetOwnerId() const { return m_ownerId; }
    
    /**
     * @brief 设置所有者ID
     * @param ownerId 所有者ID
     */
    void SetOwnerId(uint64_t ownerId) { m_ownerId = ownerId; }
protected:
    /** @brief 玩家指针 */
    NFPlayer* m_pPlayer;
    /** @brief 操作原因 */
    int m_iOpReason;
    /** @brief 日志参数 */
    int64_t m_llLogParam;
    /** @brief 子原因 */
    int m_iSubReason;

    /** @brief 金钱变更列表（一次金钱操作只会改变很少的金钱类型，不需要复杂的bsearch map等） */
    NFShmVector<MoneyChange, MAX_MONEY_TYPE_NUM> m_allMoney;

    /** @brief 能否扣成负的（默认不可以） */
    bool m_bDecToLessZero;

    /** @brief 是否忽略获取上限（不检测获取上限，也不会累加获取上限） */
    bool m_bIgnoreLimit;
private:
    /**
     * @brief 预检查提交
     * @param bSyncToClient 是否同步到客户端
     * @return 0成功，非0失败
     */
    int PreCheckCommit(bool bSyncToClient);
    
    /**
     * @brief 填充提交日志
     * @param nflog 日志对象
     * @return 0成功，非0失败
     */
    int CommitFill(NFLogPlayerMoneyFlow& nflog);

    /**
     * @brief 提交虚拟货币（只是借用了货币类型，未记录在货币数组中）
     * @param nflog 日志对象
     * @return 0成功，非0失败
     */
    int CommitVirtual(NFLogPlayerMoneyFlow& nflog);
    
    /**
     * @brief 提交真实货币（存在玩家身上）
     * @param nflog 日志对象
     * @return 0成功，非0失败
     */
    int CommitReal(NFLogPlayerMoneyFlow& nflog);
    
    /**
     * @brief 特殊途径确认需要检测周、天或者其他货币添加上限
     * @param enMoneyType 金钱类型
     * @return true需要检查上限，false不需要
     */
    bool IsAddMoneyLimit(int enMoneyType);

    /**
     * @brief 扣除预缴
     * @return 0成功，非0失败
     */
    int Disgorgment();
};
